package com.xueyi.starter.file.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.xueyi.core.utils.LogUtil;
import com.xueyi.starter.file.config.MinioAutoConfiguration;
import io.minio.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 描述 Minio工具类
 *
 * @auther 俞烨
 * @date 2022-11-24 13
 */
@Component
@AllArgsConstructor
@Slf4j
@DependsOn(value = {"minioAutoConfiguration"})
public class MinioUtil {

    public static final Map<String, Boolean> BUCKET_EXISTS = new ConcurrentHashMap<>();
    private final MinioClient minioClient;
    private final LogUtil logUtil;

    @Resource
    private MinioAutoConfiguration minioAutoConfiguration;

    @PostConstruct
    public void minioInit() throws Exception {
        createBucket(minioAutoConfiguration.getFileMinioSystemAutoProperties().getDefaultBucketName());
    }

    /**
     * 处理解析的URL
     *
     * @param url
     * @return
     */
    public String parseUrl(String url) {
        return minioAutoConfiguration.getFileMinioSystemAutoProperties().getUrl() + url;
    }

    /**
     * 判断桶是否存在
     *
     * @param bucketName
     * @return
     * @throws Exception
     */
    public Boolean bucketExists(String bucketName) throws Exception {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 创建桶
     *
     * @param bucketName 只能由小写字母、数字、句点 (.) 和连字符 (-) 组成
     * @throws Exception
     */
    public void createBucket(String bucketName) throws Exception {

        if (BUCKET_EXISTS.containsKey(bucketName) || bucketExists(bucketName)) {

            logUtil.warn("minio桶 {} 已存在", bucketName);
            BUCKET_EXISTS.put(bucketName, Boolean.TRUE);
            return;
        }
        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        String readOnly = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";
        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(readOnly).build());
    }

    /**
     * 上传文件
     *
     * @param file
     * @param objectName
     * @return
     * @throws Exception
     */
    public String uploadFile(MultipartFile file, String bucketName) throws Exception {

        String objectName = generateObjectName(file);
        ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder().bucket(bucketName)
                .object(objectName).stream(file.getInputStream(), -1, 10485760).contentType(file.getContentType()).build());
        return "/" + bucketName + "/" + objectName;
    }

    private String generateObjectName(MultipartFile file) {

        // 获取文件的后缀信息
        String extName = FileUtil.extName(file.getOriginalFilename());
        return IdUtil.fastSimpleUUID() + "." + extName;
    }

    /**
     * 上传到指定桶
     *
     * @param inputStream
     * @param objectName
     * @param bucketName
     * @return
     * @throws Exception
     */
    public String uploadFile(InputStream inputStream, String objectName, String bucketName) throws Exception {

        // 判断桶是否存在，桶如果不存在就创建
        createBucket(bucketName);
        ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder().bucket(bucketName)
                .object(objectName).stream(inputStream, -1, 10485760).build());
        return "/" + bucketName + "/" + objectName;
    }

    /**
     * 上传文件信息
     *
     * @param inputStream
     * @param objectName
     * @return
     * @throws Exception
     */
    public String uploadFile(InputStream inputStream, String objectName) throws Exception {
        return uploadFile(inputStream, objectName, minioAutoConfiguration.getFileMinioSystemAutoProperties().getDefaultBucketName());
    }

    /**
     * 删除文件
     *
     * @param objectName
     * @throws Exception
     */
    public void removeFile(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 复制文件
     *
     * @param objectName
     * @return
     * @throws Exception
     */
    public String copyFile(String objectName, String toName, String copyBucketName, String sourceBucketName) throws Exception {

        minioClient.copyObject(CopyObjectArgs.builder().bucket(copyBucketName)
                .object(toName)
                .source(CopySource.builder().bucket(sourceBucketName)
                        .object(objectName).build()).build());

        return "/" + sourceBucketName + "/" + toName;
    }

    /**
     * 下载文件
     *
     * @param objectName
     * @throws Exception
     */
    public void downLoadFile(String objectName, String fileOutputPath, String bucketName) throws Exception {
        minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucketName)
                .object(objectName).filename(fileOutputPath).build());
    }
}
